Chapter 1:
Introduction

They say that the third time's the charm. This is our third lesson involving Java collections, and I promise to wrap up the topic this time (especially since there are no more lessons after this one)! As you can see, it's an extensive subject. And while I can't teach you everything about it today, you will have enough knowledge when we're done to find out more about it on your own.

So far, in our discussion of collections, we've covered lists, sorted lists, comparable objects, iterators, for-each loops, and how to put them to use in a GUI application.

Today we're going to start out looking at a data structure called a map. Once we've seen how maps work, we'll look at how to choose between map types based on an application's requirements. Then we'll build one to use in our application.

We'll also go over one or two new topics about Java, such as what a wrapper is and how to use one in a Java collection.

Chapter 2:
Maps

TQA-34 --- As you learned in Lesson 10, a map is a data structure that uses two data components for each entry. One data component in a map entry is its key, the value we use to look up the entry. The other data component is its value, the information we get back when we look it up. In Lesson 10, I used the analogy of a phone book, where you look up a phone number by finding the name of the person or business you want to call. The key is the name, and the value is the phone number.

Many business applications use this type of search when looking up records. That's why someone usually asks you for your account number when you call your bank, for example, or your phone number when you call your phone company, or your student ID when you're talking to your school office. Those pieces of information are the keys to look up your records.

We're going to set up a similar structure for our basketball team application. Suppose that instead of starting at the beginning of the team and looking through the entries one by one, we want to go directly to a particular entry. The best way to do that is by using a map.

There are two types of maps in Java, and the first thing we need to do is decide which one to use for our application. In some ways, these two types function the same, and the methods that store and extract data by key look the same to us from a programming point of view. The differences between them have to do with their internal storage mechanism. I won't go into much detail about the internals—just take it from me that their internal structures make each of them better at some things and not so good at others.

The first map type we'll talk about is the HashMap class. It stores and retrieves data elements by key far more efficiently than any other collection structure. But it has one drawback: Its internal storage scheme makes it impossible to access the data in any predictable order. So operations like Next and Previous have no meaning in a HashMap.

The TreeMap, on the other hand, allows us to store and retrieve data by key much more efficiently than with any kind of list, but still not as efficiently as with a HashMap. It does have one significant advantage, though: It allows us to find an entry's next and previous entries based on the sequence of its key values.

Let me give you an example. Let's say that we want to store and retrieve the players in our player file by number. To do that, we need to read the file like we did in our earlier versions of the program, but this time we want to store the data in a map and use each player's number as her key.

First, assume that we store all the players in a HashMap using their numbers as keys. Let's also assume that the first player we look up is player number 22. If we display her information, we see that we've looked up Mother Goose. If we try to look up the next player in a HashMap, though, we could end up getting any other player. We would probably not get the player with the next number, or the player with the next name alphabetically, or the player we added to the map after Mother Goose. We could not predict who would show up next. That's because a HashMap does not allow iterators, nor does it have any kind of Next or Previous operations.

Now, in contrast, assume that instead of storing players in a HashMap, we store them in a TreeMap. We can look up player number 22, just not quite as efficiently as with a HashMap. But in this case, we can predict which player we'll get in a Next or Previous operation. If we ask for the next player, we get the next higher number, which in our file is player 31, Emily Hall (my personal favorite). If we ask for the previous player, we get player 21, Jill Spratt. Next and Previous operations get the next or previous player by key (player number). And if there is no next or previous entry, we get back a NULL player, which tells us that we have reached the end of the players.

Since we have been providing Next and Previous operations in our applications so far, using a TreeMap to store players makes the most sense so that we can continue to use those operations.

There is one more decision to make before we set up our map. What should we use for the key? We could use name, or we could use player number. (Technically, we could use any data item that is unique to the player, but these are the two ways we normally think of to identify players on a team.) Since we have already been talking about using player numbers, let's stick with that.

Now that we've got that covered, let's talk about the changes we'll need to make to our program.

Declaring a Map

The first change is right at the top of our class. Instead of using an ArrayList to store our Player objects, we'll set up a TreeMap. And since TreeMaps don't use iterators, we won't need the ListIterator declaration, either. We'll replace both of those declarations with one TreeMap declaration that looks like this:

    private TreeMap<Integer, Player> map;
    
    

This declaration has a slightly different format than the ArrayList's did. Instead of one data type in the angle brackets after the class name, there are two. That's because whenever we declare a map in Java, whether it's a TreeMap or a HashMap, we have to tell Java the key's data type and the data type of the value that the key maps to. In Java terminology, each key maps to the value that we get when we retrieve data using that key. So in our example, player number 22 (key) maps to Mother Goose (value), while 31 (key) maps to Emily Hall (value).

Using a type of Player for the data we look up makes sense; when we enter a key we want to get a Player object back. But why did I use a type of Integer for the key? The player number in the Player class is an int value; why not just use that? The answer is that all Java collections require the data they store to be objects; that applies to keys as well as values. So I substituted int's wrapper class, Integer, in its place.

This is the first wrapper class we've used in this course, so in the next chapter I'll take a moment to explain wrapper classes.

Chapter 3:
Wrapper Classes

TQA-35 --- Some situations in Java don't work with primitive data types. We've just seen one of them. Java's collections are set up so that they can hold any type of object, but they can't hold any of the primitive data types. The types stored in a collection must be subclasses of Object, Java's ultimate parent class. Primitive types don't meet that requirement.

But we still want to be able to have collections that hold primitive types, like ints. Java meets that need by providing wrapper classes for us to use in such situations. Each primitive type has a wrapper class: The wrapper class for a char type is Character; for int it's Integer, for float it's Float, and so on. An object of each wrapper type holds one value of the associated primitive type—an Integer object holds one int value, for example.

Wrapper objects' values are set in their constructors, and each wrapper class defines a typeValue() method: charValue() for the Character class and floatValue() for the Float class, to give examples. The following code snippet shows examples of creating and retrieving wrapper values, which we call boxing and unboxing in Java.

    Integer i = new Integer(5);
Character c = new Character('A');
Boolean b = new Boolean(true);
 . . .
int j = i.intValue();
char d = c.charValue();
boolean e = b.boolValue();
 . . .
	
    

When we actually store and retrieve entries with our map, then, we'll use Integer objects as keys rather than int values. You'll see more of how we do that in the code we're going to write today.

Loading the Player File

Now, let's look at the changes we need to make in our program so our application will work using a map. Nothing changes in our menus or window format. The first code we need to modify is in the OpenMenuItemListener inner class, where we open a file and load its data. We'll start by replacing the line that created our list:

    list = new ArrayList<Player>();
    
    

with one that creates a map:

    map = new TreeMap<Integer, Player>();
    
    

Our next change will be to replace the line that adds a Player to the list:

    list.add(new Player(name, nbr, position, avgPoints, avgRebounds, avgAssists));
    
    

with one that adds a Player to the map, using her number as a key:

    map.put(new Integer(nbr),
        new Player(name, nbr, position, avgPoints, avgRebounds, avgAssists));
	
    

Third, we don't need to sort our players anymore, so we can delete this line:

    Collections.sort(list);
    
    

Our fourth change will replace the code that displays the first player in the Player View tab. These are the lines we'll replace:

    lit = list.listIterator();
isForward = true;
if (lit.hasNext())
{
    Player p = lit.next();
    getPlayer(p);
}
	
    

We can also delete the declaration of the Boolean variable isForward, since we won't be using it.

Let's replace the above lines with the following line, which will call a new helper method, findPlayer(), to ask our user for a player number to display. When the user enters a number, this method will then display that player's information.

    findPlayer();
    
    

The findPlayer() method, which we'll be using again, should be added as the last method in our program:

    private void findPlayer()
{
    boolean isGoodNumber = false;
    Integer playerNum = new Integer(0);
    while (!isGoodNumber)
    {
        try
        {
            playerNum = new Integer(
                        Integer.parseInt(JOptionPane.showInputDialog(frame,
                        "Enter a player number:",
                        "Player Entry",
                        JOptionPane.QUESTION_MESSAGE)));
            isGoodNumber = true;
        }
        catch(NumberFormatException nfe)
        {
            JOptionPane.showMessageDialog(frame, 
                "That wasn't a player number!", 
                "Player Number Error", 
                JOptionPane.ERROR_MESSAGE);
        }
            
        if(isGoodNumber)
        {
            Player p = map.get(playerNum);
            if (p == null)
            {
                JOptionPane.showMessageDialog(frame, 
                    "Player number " + playerNum.intValue() + "does not exist!", 
                    "Player Number Error", 
                    JOptionPane.ERROR_MESSAGE);
                isGoodNumber = false;
            }
            else
                getPlayer(p);
        }
    }
}
	
    

Let me explain how this method works. It uses two local variables, isGoodNumber and playerNum. The main logic of the method is a loop that repeats until we find a player to display, as indicated by isGoodNumber.

Within the loop, we get a player number for our variable playerNum in the try block. In that block, we ask the user for a player number in a dialog box, then use Integer's parseInt() method to convert the user's input into an int value, which we store in playerNum. If users input something other than a number, our catch block makes sure the program doesn't blow up. The catch block tells users they entered a bad number, and then the loop repeats.

The if statement first makes sure we have a valid number; then it tries to get that player from the map using its get() method with the number as the key. If no map entry exists for that number, get() returns a null reference, we display an appropriate error dialog, and we set isGoodNumber back to false to force another try. If the map returns a Player object for the number, we call our getPlayer() helper method to display the player's information.

Once we have completed the Player View, we only have one more task to fix in OpenMenuItemListener. We need to build the Team View. We did that before with a for-each loop that processed the list, but for-each loops don't work with maps. Java maps do, however, have a method named values(), which gives us a collection that for-each loops do work with. Near the end of OpenMenuItemListener in our original program, there's this line:

    for (Player p : list)
    

We'll replace the reference to list with a call to our map's values() method, and the for-each loop will work. The updated line looks like this:

    for (Player p : map.values())
    
    

We've now finished our biggest task—loading the input file. The rest of our tasks will be simpler, as you'll see in the next chapter.

Chapter 4:
Moving Forward and Backward

The next task is to enable our user to move forward and backward through the players. This will actually turn out to be easier with a TreeMap than with an iterator. Let's deal with forward movement first, in the NextMenuItemListener inner class.

The first change in that method is to replace references to list with references to map. That means two changes in the following line:

    if (list == null || list.size() == 0)
    
    

We'll change both occurrences of list to map, so that the line looks like this:

    if (map == null || map.size() == 0)
    
    

Just as it checked for an empty or nonexistent list before, this line now checks for an empty or nonexistent map before proceeding.

Our next change is to delete the whole if statement that checks whether we were already moving forward. We don't need it anymore, so let's go ahead and delete these lines:

    if (!isForward)
{
    lit.next();
    isForward = true;
}	
	

We no longer need to check the iterator to find out if there are more players. We'll let the map tell us that. If there are no more players, it will return a null entry instead of a Player entry. So let's replace these lines:

    if (lit.hasNext())
{
    Player p = lit.next();
    getPlayer(p);
}
else
	
    

>with this one:

    Map.Entry<Integer, Player> entry = map.higherEntry(Integer.parseInt(
playerNum.getText()));

That's a long statement, isn't it? Let me break it down for you. The method that's going to get our next player is higherEntry(). That method gets the next entry in the map with a key higher than the key we give it. All we need to do is give it the currently displayed player's key, which is in the playerNum text box on the screen. So we get the text out of the text box using the getText() method, then convert it to an integer with the parseInt() method.

The catch is that higherEntry() does not return only a Player object; it returns a complete map entry with both the key and value. This means we have to create a Map.Entry object to hold it (that's the type of object the Java API tells us higherEntry() will return). The object we created in this line is named entry.

Now for the last little piece to this puzzle of moving forward: We still need to figure out if we reached the end of our players. If there is no key greater than the one already displayed, entry will be null, and we want to display a message dialog telling the user. So let's add this line in front of the block of code that displays the dialog:

    if (entry == null)
    
    

If we did not reach the end of our players, we want to display the one we just got from the higherEntry() method. After the message dialog code, add one last bit of logic:

    else
    getPlayer(entry.getValue());
    
    

Remember, since entry contains both a key and a value, we have to get the value (our Player object) out of it. The getValue() method does that for us.

That finishes up the forward movement logic. Moving backward requires almost identical changes in the PrevMenuItemListener inner class. The only difference is that instead of calling higherEntry(), we'll call lowerEntry(). Since the changes are so similar, I'll let you change your program on your own for practice. If you want to look at my changed version, you can see it at the following link:

New PrevMenuItemListener

Now you should be able to run your program, open the team file, select a player by number, and move forward and backward. Be sure to let me know in the Discussion Area if you have any trouble making it work.

Finding a Player

Don't you think it would be nice to have a way to go directly to another player if we know her number? We already have all the know-how to make that happen, so let's do it.

First, we need to tell the program we want to find a different player. The best way to do that is with a new menu option. Along with the menu option, we'll need a new listener to do its work. Let's add a new menu option to our View menu called Find a Player and a new listener named FindMenuItemListener. Since we've done all this before, I'll leave it to you. Go ahead and try it—and don't forget the findPlayer() method we created earlier. If you run into problems or aren't sure what it should look like, take a look at my code by clicking one of these links:

Find Menu Item

Find Menu Listener

And with that, believe it or not, we're done! The program opens our text file, stores its data in a map, provides forward and backward movement in the collection, and lets us find a particular player using her number. Let me know in the Discussion Area if you have problems making it work.

If you would like to take a look at my finished version, I have included it behind the following link:

The Finished Product

Chapter 5:
Wrap-Up

Can you believe it? We've not only finished another lesson, we've now finished the entire course. And I have to say, I think we've accomplished a lot. (I may be a bit biased about that, but I still think it's true.)

I hope you've learned everything you hoped to and maybe a little more.

I would also like to take this last opportunity to say thank you. Thanks for putting in the time this course took. I've enjoyed working with you on it.

Good luck with whatever you pursue after this.

Introduction to C# Programming

Learn the fundamentals of computer programming with C#, the in-demand and incredibly useful programming language that incorporates the best features of Visual Basic, C++, and Java. You'll begin with an exploration of input/output operations, decision making, looping, and object-oriented programming principles. Then, you'll gain hands-on experience using sequential data files, and you'll build your very own Graphical User Interface (GUI) application. Learn to program the right way: by using a state-of-the-art language to build impressive and professional-looking applications on your schedule and on your very own computer.

Creating Web Pages

Learn how to design, create, and post your very own site on the Internet's Web. Discover low-cost marketing techniques and search engine strategies.

Designing Effective Web Sites

These days, creating a Web site is so easy almost anyone can do it. But with all the competition on the Web, creating a site that's effective is more challenging than ever. To do that, you need to employ good design principles. Regardless of your current skills or level of knowledge, in this course you'll master the basics of Web design and learn how to build sites that are better and more effective. Get ready to take your Web design skills to the next level!


Lesson 12 FAQs

Q: Is there any way to gain the efficiency of the HashMap and still produce sequenced results?

A: There actually is a way to do both, but it makes getting the output in sequence less efficient. If you expect an application to have a vast majority of its accesses be by key, with only a few sorted requests, you could use a HashMap to store the data and provide efficient retrieval.

Getting the output in sequence would require dumping the entire collection into a list that could be sorted, then sorting to get the data in sequence. While that may sound like a lot of work, that's exactly what one company I worked with did with one of its most used sets of data involving several million records. It was stored in the equivalent of a large HashMap on disks and accessed tens of thousands of times every day in the company's online systems. Then, every night, the entire collection got dumped to a magnetic tape and sorted so that reports could be prepared with the data in sequence. So even though it might not be the most efficient way to get sorted data, other requirements like online system performance may dictate the most efficient storage possible for access by key.

Lesson 12 Assignment

For your last assignment, I'm going to challenge you to design and build your own GUI application. We've done two complete GUI designs in the last few lessons, one for pizza and one for a team roster. Now put all that we've learned to work for you and design a small GUI related to something that you're familiar with. It can be from school, from work, from home, or straight out of your imagination. It doesn't have to be very complex, but you should design it to accomplish a real task, no matter how small or trivial it may seem.

Come to the Discussion Area to share your ideas or get help coming up with an idea. Also let me know if you need any help designing or implementing your ideas.

I look forward to hearing about what you decide to do!

Lesson 12 Quiz Answers

1. Which map implementation is most efficient at using a key to retrieve a data value?
A HashMap.

2. Which map implementation allows us to retrieve values in key sequence as well as directly by key?
The TreeMap.

3. Which of the declarations below will correctly create a TreeMap using String objects as keys and returning Player objects as data values?
TreeMap tm = new TreeMap();

4. Which of the following declarations will create an Integer wrapper object containing a value of 99?
Integer i = new Integer(99);

5. Which of the following statements will look up my telephone number in a map named phoneBook, which uses a String containing a name as its key and returns the phone number in another String?
String number = phoneBook.get("Hall, Merrill");


Lesson 12 Supplementary Materials